
#include <BSpace>
#include <BStream>
#include <BGroup>
#include <BNode>
#include <BPhysics>
#include "member_BNode.h"
#include "member_BObject.h"
#include "member_BGroup.h"

using namespace BWE;

#define member					(*(member_BNode*)_ptr)
#define member_allocate()		_ptr = new member_BNode(this)
#define member_release()		delete (member_BNode*)_ptr

BNode::BNode()
{
	member_allocate();
}
BNode::BNode(const BString& name) : BObject(name)
{
	member_allocate();
}
BNode::BNode(const BNode& other) : BObject(other)
{
	member_allocate();
	this->copy(other);
}
BNode::~BNode()
{
	for (int i = 0; i < member.shapes.size(); i++)
	{
		BShape* shape = member.shapes[i];
		member.disconnect(shape);
	}
	member.shapes.clear();
	member_release();
}

void BNode::copy(const BNode& other)
{
	member.visible = node_member(&other)->visible;
	member.matrix = node_member(&other)->matrix;
	member.box = node_member(&other)->box;
	member.dirtyBox = node_member(&other)->dirtyBox;
	member.space = node_member(&other)->space;
	member.dirtySpace = node_member(&other)->dirtySpace;
	member.physics = node_member(&other)->physics;
	member.shapes = node_member(&other)->shapes;
	member.shapeSpaceTree = node_member(&other)->shapeSpaceTree;
}
BNode* BNode::clone() const
{
	return new BNode(*this);
}

void BNode::setGroup(BGroup* group)
{
	if (group && member.group != group)
	{
		group->insert(this);
	}
}
BGroup* BNode::group() const
{
	return member.group;
}

void BNode::setVisible(bool visible)
{
	if (member.visible != visible)
	{
		member.visible = visible;
		if (visible)
			emit(Signal_Show);
		else
			emit(Signal_Hide);
		emit(Signal_Fresh);
	}
}
bool BNode::visible() const
{
	return member.visible;
}

void BNode::setOptimized(bool optimized)
{
	if (optimized && !member.shapeSpaceTree)
	{
		member.shapeSpaceTree = new ShapeOctTree();
		member.slotFreshShapeSpaceTree();
		member.connect(this, Signal_SpaceChanged, &member_BNode::slotFreshShapeSpaceTree);
		return;
	}
	if (!optimized && member.shapeSpaceTree)
	{
		member.disconnect(this, Signal_SpaceChanged);
		member.shapeSpaceTree = 0;
		return;
	}
}
bool BNode::optimized() const
{
	return member.shapeSpaceTree.valid();
}

void BNode::move(const BVector& move)
{
	BMatrix matrix = member.matrix;
	matrix.setPosition(matrix.position() + move);
	setMatrix(matrix);
}
void BNode::move(BReal x, BReal y, BReal z)
{
	BMatrix matrix = member.matrix;
	matrix.setPosition(matrix.position() + BVector(x, y, z));
	setMatrix(matrix);
}

void BNode::setPosition(BReal x, BReal y, BReal z)
{
	BMatrix matrix = member.matrix;
	matrix.setPosition(x, y, z);
	setMatrix(matrix);
}
void BNode::setPosition(const BVector& position)
{
	BMatrix matrix = member.matrix;
	matrix.setPosition(position);
	setMatrix(matrix);
}
BVector BNode::position() const
{
	return member.matrix.position();
}

void BNode::setRotate(BReal rx, BReal ry, BReal rz)
{
	BQuater xrotate(BVector(1, 0, 0), rx);
	BQuater yrotate(BVector(0, 1, 0), ry);
	BQuater zrotate(BVector(0, 0, 1), rz);
	BMatrix matrix = member.matrix;
	matrix.setRotate(xrotate * yrotate * zrotate);
	setMatrix(matrix);
}
void BNode::setRotate(BReal qx, BReal qy, BReal qz, BReal qw)
{
	BMatrix matrix = member.matrix;
	matrix.setRotate(qx, qy, qz, qw);
	setMatrix(matrix);
}
void BNode::setRotate(const BQuater& quater)
{
	BMatrix matrix = member.matrix;
	matrix.setRotate(quater);
	setMatrix(matrix);
}
BQuater BNode::rotate() const
{
	return member.matrix.rotate();
}

void BNode::setScale(BReal x, BReal y, BReal z)
{
	BMatrix matrix = member.matrix;
	matrix.setScale(x, y, z);
	setMatrix(matrix);
}
void BNode::setScale(const BVector& scale)
{
	BMatrix matrix = member.matrix;
	matrix.setScale(scale);
	setMatrix(matrix);
}
BVector BNode::scale() const
{
	return member.matrix.scale();
}

bool BNode::setMatrix(const BMatrix& matrix)
{
	if (member.matrix != matrix)
	{
		member.matrix = matrix;
		member.dirtySpace = true;
		emit(Signal_Transformed);
		return true;
	}
	return false;
}
const BMatrix& BNode::matrix() const
{
	return member.matrix;
}

void BNode::setPhysics(BPhysics* physics)
{
	if (member.physics != physics)
	{
		member.physics = physics;
		this->emit(Signal_PhysicsChanged, physics);
	}
}
BPhysics* BNode::physics() const
{
	return member.physics;
}

bool BNode::addShape(BShape* shape)
{
	if (!shape)
		return false;
	if (!member.shapes.contain(shape))
	{
		member.shapes.append(shape);
		member.connect(shape, Signal_Fresh, &member_BNode::slotShapeFresh);
		member.connect(shape, Signal_Dirty, &member_BNode::slotShapeDirty);
		member.connect(shape, Signal_Transformed, &member_BNode::slotShapeTransform);
		member.dirtySpace = true;
		member.dirtyBox = true;
		if (BGroup* group = member.group)
		{
			group_member(group)->raiseSignal(Signal_Insert, shape);
		}
		emit(Signal_Insert, shape);
		emit(Signal_Dirty);
		return true;
	}
	return false;
}
bool BNode::removeShape(BShape* shape)
{
	if (!shape)
		return false;
	if (member.shapes.contain(shape))
	{
		BShapeHolder holder = shape;
		member.shapes.remove(shape);
		member.disconnect(shape);
		member.dirtySpace = true;
		member.dirtyBox = true;
		if (BGroup* group = member.group)
		{
			group_member(group)->raiseSignal(Signal_Remove, shape);
		}
		emit(Signal_Remove, shape);
		emit(Signal_Dirty);
		return true;
	}
	return false;
}
void BNode::clearShapes()
{
	for (int i = 0; i < member.shapes.size(); i++)
	{
		BShape* shape = member.shapes[i];
		member.disconnect(shape);
		emit(Signal_Remove, shape);
	}
	member.shapes.clear();
	member.dirtySpace = true;
	member.dirtyBox = true;
	emit(Signal_Dirty);
}

bool BNode::contain(const BShape* shape) const
{
	return member.shapes.contain(shape);
}

int BNode::shapeCount() const
{
	return member.shapes.size();
}
BShape* BNode::shape(int index)
{
	return member.shapes(index);
}
const BShape* BNode::shape(int index) const
{
	return member.shapes(index);
}

BShape* BNode::shape(const BString& name)
{
	for (int i = 0; i < member.shapes.size(); i++)
	{
		BShape* shape = member.shapes[i];
		if (shape->name() == name)
			return shape;
	}
	return 0;
}
const BShape* BNode::shape(const BString& name) const
{
	for (int i = 0; i < member.shapes.size(); i++)
	{
		BShape* shape = member.shapes[i];
		if (shape->name() == name)
			return shape;
	}
	return 0;
}

const BBox& BNode::box() const
{
	if (member.dirtyBox)
	{
		member.box = ((BNode*)this)->flush();
		member.dirtyBox = false;
		member.dirtySpace = true;
		emit(Signal_Flushed);
	}
	return member.box;
}
const BSpace& BNode::space() const
{
	if(member.dirtySpace)
	{
		BBox box = this->box() * member.matrix;
		member.space.reset(box.center());
		member.space.expand(box.v0());
		member.space.expand(box.v1());
		member.space.expand(box.v2());
		member.space.expand(box.v3());
		member.space.expand(box.v4());
		member.space.expand(box.v5());
		member.space.expand(box.v6());
		member.space.expand(box.v7());
		member.dirtySpace = false;
		emit(Signal_SpaceChanged);
	}
	return member.space;
}

void BNode::dirty()
{
	member.dirtyBox = true;
	emit(Signal_Dirty);
}
BBox BNode::flush()
{
	BSpace space;
	for (int i = 0; i < member.shapes.size(); i++)
	{
		BShape* shape = member.shapes[i];
		space.expand(shape->space());
	}
	return space;
}
void BNode::clear()
{
	member.physics = 0;
	clearShapes();
}

bool BNode::constrained(BConstraint* constraint)
{
	return member.constraintRefs.contain(constraint);
}

int BNode::constraintRefCount()
{
	return member.constraintRefs.size();
}
BConstraint* BNode::constraintRef(int index)
{
	return member.constraintRefs(index);
}
const BConstraint* BNode::constraintRef(int index) const
{
	return member.constraintRefs(index);
}
